"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Utils_1 = require("./Utils");
var FairyEditor = CS.FairyEditor;
const CodeWriter_1 = require("./CodeWriter");
const File = CS.System.IO.File;
const Path = CS.System.IO.Path;
function setExportBundleInfo(handler) {
    const dir = handler.exportCodePath;
    const name = handler.ToFilename(handler.pkg.name);
    // 设置Bundle信息，添加依赖
    const metaFile = dir + ".meta";
    if (!File.Exists(metaFile)) {
        console.error('无法为导出目录设置Bundle信息，请刷新Creator后重新导出！');
        return;
    }
    const json = File.ReadAllText(metaFile);
    let meta = JSON.parse(json);
    let userData = meta.userData;
    if (!userData)
        userData = {};
    userData.isBundle = true;
    userData.bundleName = Utils_1.generatePackageScriptBundleName(name, false);
    userData.auto_exclude = true;
    userData.priority = Math.max(userData.priority || 1, 5);
    meta.userData = userData;
    File.WriteAllText(metaFile, JSON.stringify(meta, null, 2));
}
function generateAutoBind(handler, exportSameWithRes) {
    const dir = handler.exportCodePath;
    const name = handler.ToFilename(handler.pkg.name);
    const indexFile = Path.Combine(dir, exportSameWithRes ? "binder.ts" : "index.ts");
    if (File.Exists(indexFile))
        return; // 不覆盖
    // 生成index.ts
    let context = `import { binder } from './exported/${name}Binder';

`;
    if (exportSameWithRes) {
        context += `export function ${Utils_1.generateBindAllFunctionName(handler)}() {
    binder.bindAll();
}`;
    }
    else {
        context += "binder.bindAll();";
    }
    File.WriteAllText(indexFile, context);
}
function getClassNameByItem(prefix, handler, item) {
    let name = `${prefix}${handler.ToFilename(item.owner.name)}_${handler.ToFilename(item.name)}`;
    if (item.branch !== "") {
        name += "_" + item.branch;
    }
    return name;
}
function getMemberType(prefix, handler, memberInfo) {
    const item = memberInfo.res;
    if (!item)
        return memberInfo.type;
    if (!item.owner.publishSettings.genCode)
        return memberInfo.type;
    if (item.type !== 'component')
        return memberInfo.type;
    return getClassNameByItem(prefix, handler, memberInfo.res);
}
function genCode(handler, exportSameWithRes) {
    let settings = handler.project.GetSettings("Publish").codeGeneration;
    let codePkgName = handler.ToFilename(handler.pkg.name); //convert chinese to pinyin, remove special chars etc.
    let exportCodePath = Path.Combine(handler.exportCodePath, "exported");
    let namespaceName = codePkgName;
    let ns = "fgui";
    let isThree = handler.project.type == FairyEditor.ProjectType.ThreeJS;
    let isCC = handler.project.type == FairyEditor.ProjectType.CocosCreator;
    if (settings.packageName)
        namespaceName = settings.packageName + '.' + namespaceName;
    //CollectClasses(stripeMemeber, stripeClass, fguiNamespace)
    let classes = handler.CollectClasses(settings.ignoreNoname, settings.ignoreNoname, ns);
    handler.SetupCodeFolder(exportCodePath, "ts"); //check if target folder exists, and delete old files
    let codedClasses = new Set();
    let classCnt = classes.Count;
    for (let i = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        codedClasses.add(classInfo.className);
    }
    let mainClasses = new Map();
    let branchClasses = [];
    let getMemberByName = settings.getMemberByName;
    let classes2 = handler.CollectClasses(settings.ignoreNoname, false, ns);
    let classCnt2 = classes2.Count;
    let writer = new CodeWriter_1.default({ blockFromNewLine: false, usingTabs: true });
    for (let i = 0; i < classCnt2; i++) {
        let classInfo = classes2.get_Item(i);
        let members = classInfo.members;
        writer.reset();
        const fileName = classInfo.res.branch == "" ? classInfo.className : `${classInfo.className}_${classInfo.res.branch}`;
        const isCoded = codedClasses.has(classInfo.className);
        let codedInfo = { coded: isCoded };
        if (classInfo.res.branch !== "") {
            let branchInfo = codedInfo;
            branchInfo.branch = classInfo.res.branch;
            branchInfo.className = classInfo.className;
            branchInfo.members = members;
            branchClasses.push(branchInfo);
            continue;
        }
        else {
            codedInfo.codedMembers = new Map();
            mainClasses.set(classInfo.className, codedInfo);
        }
        const className = getClassNameByItem(settings.classNamePrefix, handler, classInfo.res);
        if (isThree) {
            writer.writeln('import * as fgui from "fairygui-three";');
            writer.writeln();
        }
        else if (isCC) {
            writer.writeln('import * as fgui from "db://fairygui-xforge/FairyGUI";');
            writer.writeln();
        }
        writer.writeln('declare global');
        writer.startBlock();
        writer.writeln('// @ts-ignore');
        writer.writeln('interface %s extends %s {}', className, isCoded ? fileName : classInfo.superClassName);
        writer.endBlock();
        if (isCoded) {
            writer.writeln('export default class %s extends %s', fileName, classInfo.superClassName);
            writer.startBlock();
            writer.writeln();
            let memberCnt = members.Count;
            for (let j = 0; j < memberCnt; j++) {
                let memberInfo = members.get_Item(j);
                writer.writeln('public %s:%s;', memberInfo.varName, getMemberType(settings.classNamePrefix, handler, memberInfo));
            }
            writer.writeln('public static URL:string = "ui://%s%s";', handler.pkg.id, classInfo.resId);
            writer.writeln();
            writer.writeln('public static createInstance():%s', className);
            writer.startBlock();
            writer.writeln('return <%s>(%s.UIPackage.createObject("%s", "%s"));', className, ns, handler.pkg.name, classInfo.resName);
            writer.endBlock();
            writer.writeln();
            writer.writeln('protected onConstruct():void');
            writer.startBlock();
            for (let j = 0; j < memberCnt; j++) {
                let memberInfo = members.get_Item(j);
                let memCodeInfo = {
                    name: memberInfo.name,
                    group: memberInfo.group,
                    index: memberInfo.index
                };
                codedInfo.codedMembers.set(memberInfo.varName, memCodeInfo);
                if (memberInfo.group == 0) {
                    memCodeInfo.class = getMemberType(settings.classNamePrefix, handler, memberInfo);
                    if (getMemberByName)
                        writer.writeln('this.%s = <%s>(this.getChild("%s"));', memberInfo.varName, getMemberType(settings.classNamePrefix, handler, memberInfo), memberInfo.name);
                    else
                        writer.writeln('this.%s = <%s>(this.getChildAt(%s));', memberInfo.varName, getMemberType(settings.classNamePrefix, handler, memberInfo), memberInfo.index);
                }
                else if (memberInfo.group == 1) {
                    if (getMemberByName)
                        writer.writeln('this.%s = this.getController("%s");', memberInfo.varName, memberInfo.name);
                    else
                        writer.writeln('this.%s = this.getControllerAt(%s);', memberInfo.varName, memberInfo.index);
                }
                else {
                    if (getMemberByName)
                        writer.writeln('this.%s = this.getTransition("%s");', memberInfo.varName, memberInfo.name);
                    else
                        writer.writeln('this.%s = this.getTransitionAt(%s);', memberInfo.varName, memberInfo.index);
                }
            }
            writer.endBlock();
            writer.endBlock(); //class
        }
        writer.save(exportCodePath + '/' + fileName + '.ts');
    }
    writer.reset();
    let binderName = codePkgName + 'Binder';
    for (let i = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        if (classInfo.res.branch !== "")
            continue;
        const fileName = classInfo.res.branch == "" ? classInfo.className : `${classInfo.className}_${classInfo.res.branch}`;
        writer.writeln('import %s from "./%s";', fileName, fileName);
    }
    if (isThree) {
        writer.writeln('import * as fgui from "fairygui-three";');
    }
    else if (isCC) {
        writer.writeln('import * as fgui from "db://fairygui-xforge/FairyGUI";');
        writer.writeln();
    }
    writer.writeln();
    writer.writeln(`class ${binderName} extends fgui.UIBinder`);
    writer.startBlock();
    writer.writeln('constructor()');
    writer.startBlock();
    writer.writeln('super();');
    for (let i = 0; i < classCnt; i++) {
        let classInfo = classes.get_Item(i);
        if (classInfo.res.branch !== "")
            continue;
        const fileName = classInfo.res.branch == "" ? classInfo.className : `${classInfo.className}_${classInfo.res.branch}`;
        writer.writeln(`this.register(${fileName}.URL, ${fileName});`);
    }
    writer.endBlock(); //register
    writer.endBlock(); //class
    writer.writeln(`export const binder = new ${binderName}();`);
    writer.save(exportCodePath + '/' + binderName + '.ts');
    generateAutoBind(handler, exportSameWithRes);
    if (!exportSameWithRes) // 当与Res目录相同时，不需重复设置meta
        setExportBundleInfo(handler);
    // 按照FGUI默认的规则，分支组件扩展复用主干组件的扩展，所以要求分支组件扩展元素包含主干导出的所有元素
    for (const cls of branchClasses) {
        const mainInfo = mainClasses.get(cls.className);
        if (!mainInfo) {
            throw new Error(`Main class ${cls.className} not found`);
        }
        if (mainInfo.coded !== cls.coded) {
            throw new Error(`Main class ${cls.className} and branch class ${cls.className} should have the same code status`);
        }
        const memCount = cls.members.Count;
        for (let j = 0; j < memCount; j++) {
            const memberInfo = cls.members.get_Item(j);
            const mainMemInfo = mainInfo.codedMembers.get(memberInfo.varName);
            if (!mainMemInfo) {
                // throw new Error(`Main class ${cls.className} doesn't have member ${memberInfo.varName}`);
                continue; // 允许缺少成员声明
            }
            if (mainMemInfo.group !== memberInfo.group) {
                throw new Error(`Main class ${cls.className} and branch class ${cls.className} should have the same member ${memberInfo.varName} type`);
            }
            if (getMemberByName) {
                if (mainMemInfo.name !== memberInfo.name) {
                    throw new Error(`Main class ${cls.className} and branch class ${cls.className} should have the same member ${memberInfo.varName} name`);
                }
            }
            else {
                if (mainMemInfo.index !== memberInfo.index) {
                    throw new Error(`Main class ${cls.className} and branch class ${cls.className} should have the same member ${memberInfo.varName} index`);
                }
            }
            if (memberInfo.group == 0) {
                const className = getMemberType(settings.classNamePrefix, handler, memberInfo);
                if (className !== mainMemInfo.class) {
                    throw new Error(`Main class ${cls.className} and branch class ${cls.className} should have the same member ${memberInfo.varName} class`);
                }
            }
            mainInfo.codedMembers.delete(memberInfo.varName);
        }
        if (mainInfo.codedMembers.size > 0) {
            throw new Error(`Branch class ${cls.className} doesn't have members ${Array.from(mainInfo.codedMembers.keys())}`);
        }
    }
}
exports.genCode = genCode;
